#include <STM32FreeRTOS.h>
#include "hwboard.h"
#include "plc_abi.h"

#define XP1 PG1
#define XP2 PE8
#define XP3 PE12
#define XP4 PE14
#define XP5 PE7
#define XP6 PG0
#define XP7 PD14
#define XP8 PG2
#define XP9 PG4
#define XP10 PG6
#define XP11 PD15
#define XP12 PG3

#define YP1 PA5
#define YP2 PA6
#define YP3 PE9
#define YP4 PD12
#define YP5 PC6
#define YP6 PE5
#define YP7 PF6
#define YP8 PF7
#define YP9 PF8
#define YP10 PF9
#define YP11 PA0
#define YP12 PE11
#define YP13 PD13
#define YP14 PB0
#define YP15 PB1
#define YP16 PE13
#define YP17 PF11
#define YP18 PF12
#define YP19 PF13
#define YP20 PF14

#include <pt100_hx711.h>

pt100_hx711 hx1(PF2, PC15);
pt100_hx711 hx2(PF10, PF5);
pt100_hx711 hx3(PA4, PC3);
pt100_hx711 hx4(PC14, PC13);
pt100_hx711 hx5(PF4, PF3);
pt100_hx711 hx6(PC2, PC0);
pt100_hx711 hx7(PE0, PB9);
pt100_hx711 hx8(PE4, PE3);
pt100_hx711 hx9(PB8, PB7);
pt100_hx711 hx10(PE2, PE1);

#include "swspi.h"
#include "ltc2496.h"

SWSPI spi(PD9, PD10, PD11, PD8, 1, 0);
ltc2496 ad;
const u8 chan[] = {0, 1, 2, 8, 9, 10, 3, 11, 4, 12, 5, 13, 6, 14, 7, 15};

#include "I2C_Driver.h"
#include <ch423.h>

TwoWire i2c(PF0, PF1);
ch423 ich423(&i2c);

#include <MCPDAC.h>

MCPDACx4 mcp;

#include <RA165.h>

#define DI_SH PC9
#define DI_SK PE6
#define DI_QH PC8

RA165 ra165(DI_SK, DI_QH);

#include <ShiftRegister74HC595.h>

ShiftRegister74HC595 sr(3, PA15, PD3, PD1);

#include "SPI.h"

nSPI spi1(PB15, PB14, PB10);

#include "SPIFlash.h"

SPIFlash fspi(&spi1, PC7);
#ifdef USE_LFS
FlashFs flash_fs;
#endif

const uint8_t pin_beep = PB3;
#ifdef USE_USBH

#include "usbstick.h"

USBStick usb;
#endif



void httpd_setup();

#include <plc_rte.h>
#include <logger_rte.h>
#include <kSerial.h>
#include <retain_kvdb.h>
#include <plc_var_class.h>

#ifdef PPP_SUPPORT

#include <ppp_class.h>

ppp_class ppp;
#endif

//#include "AccelStepper.h"
//
//AccelStepper as[] = {AccelStepper(TIM2, YP1, YP11), AccelStepper(TIM3, YP2, YP12), AccelStepper(TIM1, YP3, YP13),
//                     AccelStepper(TIM4, YP4, YP14), AccelStepper(TIM8, YP5, YP15), AccelStepper(TIM9, YP6, YP16),
//                     AccelStepper(TIM10, YP7, YP17), AccelStepper(TIM11, YP8, YP18), AccelStepper(TIM13, YP9, YP19),
//                     AccelStepper(TIM14, YP10, YP20)};
HardwareTimer pwm(TIM13);
#ifdef PLC
extern "C" int __init();
#endif

kSerial k3(PC11, PC10);
kSerial k2(PA3, PD5);
kSerial k5(PD2, PC12);
kSerial k1(PA10, PA9);
kSerial k6(PG9, PG14);
kSerial *kSerial::Serials[] = {&k3, &k2, &k5, &k1, &k6};
const u8 kSerial::Serials_nrs = 5;

hwboard::hwboard() {
    extern int VECTOR_ISR;
    SCB->VTOR = (uint32_t) &VECTOR_ISR;
    plc_var.info.status = 1;
    plc_var.info.toInit = 0;
    k1.begin(115200);
}

extern "C"
{
void eth_reset() {
    // ETH RESET
    pinMode(PE10, OUTPUT);
    digitalWrite(PE10, LOW);
    vTaskDelay(100);
    digitalWrite(PE10, HIGH);
    vTaskDelay(100);
}
}

void hwboard::beep(bool onoff) {
    if (onoff)
        digitalWrite(pin_beep, HIGH);
    else
        digitalWrite(pin_beep, LOW);
}

extern "C" void SMI_Init();
extern "C" void KSZ8863_Print_Reg_Value();
extern "C" uint8_t ksz8863_reg(uint8_t addr);

int hwboard::begin(u32 tick) {
    pinMode(pin_beep, OUTPUT);
    digitalWrite(pin_beep, HIGH);

    pinMode(LED_RUN, OUTPUT);
    pinMode(LED_ERR, OUTPUT);
    pinMode(LED_ETH, OUTPUT);
    digitalWrite(LED_ERR, HIGH);
    digitalWrite(LED_RUN, HIGH);
    digitalWrite(LED_ETH, HIGH);

    // delay(10);
    // i2c.begin(MASTER_ADDRESS);
    delay(10);
    ich423.begin();
    rte.set_state(PLC_STATUS::Booting);
    // pcf8563.initClock();

    // ch423_test();
    fspi.init();
#ifdef USE_LFS
    flash_fs.begin(&fspi, LFS_MB * 256);
    flash_fs.dir();
#endif
    beep(0);
    eth_reset();

#ifdef PLC
#ifdef ONLINE_DEBUG
    // plc_debug_init();
#endif
#endif
#ifdef USE_USBH
    logger.debug("usb begin");
    usb.begin();
    logger.debug("usb begined");
#endif

    enOut = 1;
    enInput = 1;
    pwm.setPWM(1, YP9, 1000000, 50);
    pinMode(PD0, OUTPUT); // OE595
    hx1.begin();
    // hx2.begin();
    hx3.begin();
    hx4.begin();
    hx5.begin();
    hx6.begin();
    hx7.begin();
    hx8.begin();
    hx9.begin();
    hx10.begin();
    digitalWrite(PD0, LOW);
    sr.begin();
    sr.setAllLow();

    mcp.begin(PB6, PB4, PG11, PG13, PG10, PG12, PG15);
    mcp.setGain(0, true);
    mcp.setGain(1, true);
    mcp.ShutDown(0, false);
    mcp.ShutDown(1, false);

    ad.begin(&spi, chan, 10, 0);
    pinMode(XP1, INPUT);
    pinMode(XP2, INPUT);
    pinMode(XP3, INPUT);
    pinMode(XP4, INPUT);
    pinMode(XP5, INPUT);
    pinMode(XP6, INPUT);
    pinMode(XP7, INPUT);
    pinMode(XP8, INPUT);
    pinMode(XP9, INPUT);
    pinMode(XP10, INPUT);
    pinMode(XP11, INPUT);
    pinMode(XP12, INPUT);
    return boardBase::begin(tick);
};

int hwboard::get_dbg_jmpr_get() {
    // return digitalRead(PA9) ? 0 : 1;
    return 0;
}

int hwboard::get_rst_jmpr_get() {
    // return digitalRead(PA10) ? 0 : 1;
    return 0;
}

#undef poll

void hwboard::ioled() {
    if (plc_var.info.plc_state == plc_state && plc_var.info.plc_state != (u8) PLC_STATUS::Started)
        return;
    plc_state = plc_var.info.plc_state;
    bool changed = false;
#ifdef USE_CH423
    switch (plc_var.info.plc_state) {
        case (u8) PLC_STATUS::HARDWARE_ERROR:
            ich423.setChar('F', 2);
            break;
        case (u8) PLC_STATUS::Broken:
            ich423.setChar('B', 2);
            break;
        case (u8) PLC_STATUS::Empty:
            ich423.setChar('N', 2);
            break;
        case (u8) PLC_STATUS::Disconnected:
            ich423.setChar('d', 2);
            break;
        case (u8) PLC_STATUS::Ymodem:
            ich423.setChar('Y', 2);
            break;
        case (u8) PLC_STATUS::Tftpd:
            ich423.setChar('T', 2);
            break;
        case (u8) PLC_STATUS::Booting:
            ich423.setChar('b', 1);
            break;
        case (u8) PLC_STATUS::PLC_ERROR:
            ich423.setChar('E', 2);
            break;
        case (u8) PLC_STATUS::Restart:
            ich423.setChar('r', 2);
            break;
        case (u8) PLC_STATUS::FLASH_FORMAT:
            ich423.setChar('F', 3);
            break;
        case (u8) PLC_STATUS::USB_INSERTED:
            ich423.setChar('U', 3);
            break;
        case (u8) PLC_STATUS::USB_COPYING:
            ich423.setChar('C', 3);
            break;
        case (u8) PLC_STATUS::USB_COPYED:
            ich423.setChar('D', 1);
            break;
        case (u8) PLC_STATUS::USB_NO_FILE:
            ich423.setChar('n', 2);
            break;

        case (u8) PLC_STATUS::APP_FLASH_END:
            ich423.setChar('E', 3);
            break;
        case (u8) PLC_STATUS::APP_FLASHING:
            ich423.setChar('W', 1);
            break;
        case (u8) PLC_STATUS::APP_FLASH_BEGIN:
            ich423.setChar('P', 1);
            break;
        case (u8) PLC_STATUS::Stopped:
            ich423.setChar('P', 2);
            break;
        case (u8) PLC_STATUS::Started:
            if (plc_var.info.prompt > 0x20 && millis() & 0x800) {
                ich423.setChar(plc_var.info.prompt, 2);
            } else {
                for (int i = 0; i < (IO_XI_NRS + IO_YO_NRS) / 8; i++) {
                    if (plc_var.digitalValue.xx[i] != digitalValue[i]) {
                        changed = true;
                        digitalValue[i] = plc_var.digitalValue.xx[i];
                    }
                }
                if (plc_var.info.alarm != alarm) {
                    changed = true;
                    alarm = plc_var.info.alarm;
                }
                if (plc_var.info.status != status) {
                    changed = true;
                    status = plc_var.info.status;
                }
                if (changed) {
                    for (int i = 0; i < 16; i++) {
                        char v = plcVar.digitalValue(IO_XI + i);
                        ich423.setBit(i / 8, i % 8, v);
                        }
                    for (int i = 0; i < 8; i++) {
                        char v = plc_var.info.alarm & (1 << i);
                        ich423.setBit(2, i, v ? 2 : 0);
                    }
                    for (int i = 0; i < 24; i++) {
                        char v = plcVar.digitalValue(IO_YO + i);
                        ich423.setBit(i / 8 + 3, i % 8, v);
                }
                for (int i = 0; i < 8; i++) {
                    char v = plc_var.info.alarm & (1 << i);
                        ich423.setBit(6, i, v ? 2 : 0);
                }
                for (int i = 0; i < 8; i++) {
                    char v = plc_var.info.status & (1 << i);
                        ich423.setBit(7, i, v ? 3 : 0);
                    }
                }
            }
            break;
        default:
            break;
    }
    if (changed)
        ich423.update();
#endif
}

const u8 DoTable[24] = {0, 3, 1, 2, 4, 7, 5, 6, 0 + 8, 3 + 8, 1 + 8, 2 + 8, 4 + 8, 7 + 8, 5 + 8, 6 + 8,
                        0 + 16, 3 + 16, 1 + 16, 2 + 16, 4 + 16, 7 + 16, 5 + 16, 6 + 16};


u8 mcpTable[] = {3, 7, 1, 5, 2, 6, 0, 4};

void hwboard::board_output(u32 tick) {
    for (int i = 0; i < 24; i++) {
        char v = plcVar.Y(i) ? 1 : 0;
        u8 row = DoTable[i];
        sr.setNoUpdate(row, v);
    }

    sr.updateRegisters();
    for (int i = 0; i < 8; i++) {
        mcp.setVoltage(mcpTable[i], plc_var.analogValue.YA[i]);
    }
    mcp.update();
    ioled();
}

void hwboard::initReset() {
    rte.app_stop();
    plc_var.info.plc_curr_app->config_init();
    rte.app_start();
}

void hwboard::flashClear() {
#ifdef USE_LFS
    rte.app_stop();
    rte.set_state(PLC_STATUS::FLASH_FORMAT);
    flash_fs.clear();
    vTaskDelay(10);
    flash_fs.format();
    vTaskDelay(10);
    rte.setReboot();
    rte.set_state(PLC_STATUS::Restart);
    while (true);
#endif
}

uint32_t last_led_tick;

// if (!digitalRead(LED_RUN) && (millis() - last_led_tick) > 200)
// {
//     last_led_tick = millis();
// }
extern "C"
{
void net_led_on() {
    digitalWrite(LED_ETH, LOW);
    //       Ethernet.touch();
}
void net_led_off() {
    digitalWrite(LED_ETH, HIGH);
    //        Ethernet.touch();
}
}

#undef close

#ifdef USE_USBH

// char path[32];
void usb_ready() {
    rte.set_state(PLC_STATUS::Restart);
    rte.app_stop();
}

void usb_disconnect() {
    HAL_NVIC_SystemReset();
}

#endif

int Serial_diag(serial_t *serial) {
    logger.debug("CR1 = 0x%x\n",
                 (u32) serial->uart->CR1); /*!< USART Control register 1,                 Address offset: 0x00 */

    logger.debug("CR2 = 0x%x\n",
                 (u32) serial->uart->CR2); /*!< USART Control register 2,                 Address offset: 0x04 */
    logger.debug("CR3 = 0x%x\n",
                 (u32) serial->uart->CR3); /*!< USART Control register 3,                 Address offset: 0x08 */
    logger.debug("BRR = 0x%x\n",
                 (u32) serial->uart->BRR); /*!< USART Baud rate register,                 Address offset: 0x0C */
    logger.debug("GTPR = 0x%x\n",
                 (u32) serial->uart->GTPR); /*!< USART Guard time and prescaler register,  Address offset: 0x10 */
    // logger.debug("PA MODER=0x%x,OTYPE=0x%x", GPIOA->MODER, GPIOA->OTYPER);
    int iser = NVIC->ISER[1] & (1 << (37 - 32));
    int cr1 = USART1->CR1 & (1 << 5);
    if (cr1 == 0 || iser == 0) {
        logger.error("rx irq disable. iser=%d cr1=%d", iser, cr1);
    }
    return 0;
}

int hwboard::diag(u32 tick) {
    if (SCB->VTOR != (uint32_t) &VECTOR_ISR) {
        logger.error("VTOR:0x%x vs 0x%x\n", (uint32_t) SCB->VTOR, (uint32_t) &VECTOR_ISR);
    }

    for (int i = 0; i < 1; i++) {
        delay(100);
        logger.debug("Serial %d : \n", i);
        kSerial::get_serial(i)->diag();
        logger.debug("=======\n\n");
    }
    return 0;
}

void hwboard::plc_run_start() {
    if (digitalRead(LED_RUN) && (rtos::ticks() - last_led_tick) > 200) {
        digitalWrite(LED_RUN, LOW);
        last_led_tick = rtos::ticks();
    }
}

void hwboard::plc_run_end() {
    if (!digitalRead(LED_RUN) && (rtos::ticks() - last_led_tick) > 200) {
        digitalWrite(LED_RUN, HIGH);
        last_led_tick = rtos::ticks();
    }
}

int hwboard::Poll(u32 tick) {
    pinMode(DI_SH, OUTPUT);
    digitalWrite(DI_SH, LOW);
    for (int i = 0; i < 10; i++) {
        plc_var.analogValue32.xa32[i] = (ad.read(i));
        plc_var.analogValue.XA[i] = plc_var.analogValue32.xa32[i] >> 4;
    }
    digitalWrite(DI_SH, HIGH);
    u8 a, b, c, d;
    a = ~ra165.readByte(MSB, 1);
    b = ~ra165.readByte(MSB, 0);
    c = ~ra165.readByte(MSB, 0);
    d = ra165.readByte(MSB, 0);
    u8 xp2;
    xp2 = plc_var.digitalValue.X[0] = plc_var.digitalValue.X[1] = plc_var.digitalValue.X[2] = 0;
    for (int i = 0; i < 8; i++) {
        plc_var.digitalValue.X[2] |= xx_flt[i].poll(tick, a & (1 << i)) ? 1 << i : 0;
        plc_var.digitalValue.X[1] |= xx_flt[i + 8].poll(tick, b & (1 << i)) ? 1 << i : 0;
        plc_var.digitalValue.X[0] |= xx_flt[i + 16].poll(tick, c & (1 << i)) ? 1 << i : 0;
        xp2 |= xx_flt[i + 24].poll(tick, d & (1 << i)) ? 1 << i : 0;
    }
    // if (plc_var.digitalValue.X[0] || plc_var.digitalValue.X[1] || plc_var.digitalValue.X[2]) {
    //     core_debug("0x%x,0x%x,0x%x,0x%x\n", plc_var.digitalValue.X[0], plc_var.digitalValue.X[1],
    //                plc_var.digitalValue.X[2], xp2);
    // }
    plc_var.digitalValue.xp[0] = plc_var.digitalValue.xp[1] = plc_var.digitalValue.xp[2] = 0xff;
    plc_var.digitalValue.xp[0] &= ~(digitalRead(XP1) << 0);
    plc_var.digitalValue.xp[0] &= ~(digitalRead(XP2) << 1);
    plc_var.digitalValue.xp[0] &= ~(digitalRead(XP3) << 2);
    plc_var.digitalValue.xp[0] &= ~(digitalRead(XP4) << 3);
    plc_var.digitalValue.xp[0] &= ~(digitalRead(XP5) << 4);
    plc_var.digitalValue.xp[0] &= ~(digitalRead(XP6) << 5);
    plc_var.digitalValue.xp[0] &= ~(digitalRead(XP7) << 6);
    plc_var.digitalValue.xp[0] &= ~(digitalRead(XP8) << 7);
    plc_var.digitalValue.xp[1] &= ~(digitalRead(XP9) << 0);
    plc_var.digitalValue.xp[1] &= ~(digitalRead(XP10) << 1);
    plc_var.digitalValue.xp[1] &= ~(digitalRead(XP11) << 2);
    plc_var.digitalValue.xp[1] &= ~(digitalRead(XP12) << 3);
    plc_var.digitalValue.xp[1] &= ~(((xp2 & 0x8) ? 1 : 0) << 4);
    plc_var.digitalValue.xp[1] &= ~(((xp2 & 0x80) ? 1 : 0) << 5);
    plc_var.digitalValue.xp[1] &= ~(((xp2 & 0x1) ? 1 : 0) << 6);
    plc_var.digitalValue.xp[1] &= ~(((xp2 & 0x40) ? 1 : 0) << 7);
    plc_var.digitalValue.xp[2] &= ~(((xp2 & 2) ? 1 : 0) << 0);
    plc_var.digitalValue.xp[2] &= ~(((xp2 & 0x20) ? 1 : 0) << 1);
    plc_var.digitalValue.xp[2] &= ~(((xp2 & 4) ? 1 : 0) << 2);
    plc_var.digitalValue.xp[2] &= ~(((xp2 & 0x10) ? 1 : 0) << 3);
    plc_var.digitalValue.xp[2] &= 0xf;
    hx1.loop();
    hx2.loop();
    hx3.loop();
    hx4.loop();
    hx5.loop();
    hx6.loop();
    hx7.loop();
    hx8.loop();
    hx9.loop();
    hx10.loop();
    plc_var.analogValue32.xt[5] = hx1.value();
    plc_var.analogValue32.xt[4] = hx2.value();
    plc_var.analogValue32.xt[3] = hx3.value();
    plc_var.analogValue32.xt[2] = hx4.value();
    plc_var.analogValue32.xt[1] = hx5.value();
    plc_var.analogValue32.xt[0] = hx6.value();
    plc_var.analogValue32.xt[9] = hx7.value();
    plc_var.analogValue32.xt[8] = hx8.value();
    plc_var.analogValue32.xt[7] = hx9.value();
    plc_var.analogValue32.xt[6] = hx10.value();
    return 0;
}

int hwboard::test_hw() {
    return 0;
}

int hwboard::load_app() {
    return 0;
}

int rte_app::load() {
#ifdef USE_LFS
    board.lfs_to_flash("plc.bin", APP_START, 7, 1);
#endif
    plc_var.info.plc_curr_app = (plc_app_abi_t *) APP_START;
    return 0;
}

void hwboard::plc_run_stop() {
    digitalWrite(LED_RUN, LOW);
}

void hwboard::plc_run_error() {
    digitalWrite(LED_ERR, HIGH);
}

void hwboard::plc_run_empty() {
    digitalWrite(LED_RUN, HIGH);
}


void write_outwords_cb(uint16_t, uint16_t, uint16_t *) {
    //
}

void write_bits_cb(uint16_t, uint16_t, uint8_t *) {
    //
}

mem_block *hwboard::get_retain() {
#ifdef USE_KVDB
    return new retain_kvdb("retain1", "retain2");
#endif
    return nullptr;
}

void hwboard::led_run(int v) {
    if (v)
        digitalWrite(LED_RUN, LOW);
    else
        digitalWrite(LED_RUN, HIGH);
}

void hwboard::led_err(int v) {
    if (v)
        digitalWrite(LED_ERR, LOW);
    else
        digitalWrite(LED_ERR, HIGH);
}


kSerial *kSerial::get_serial(int fd) {
    return Serials[fd];
}

kSerial *kSerial::get_mon_serial() {
    return Serials[3];
}

kSerial *kSerial::get_firmata_serial() {
    return Serials[3];
}

const u8 boardBase::xport[] = {};
const u8 boardBase::yport[] = {};

int hwboard::dev_test(u32 tick) {
    return 0;
}

#ifdef USE_KVDB
struct fdb_default_kv_node default_kv_table[3] = {
        {"version", (void *) "2.0",   0, KV_STR_VALUE}, /* string KV */
        {"model",   (void *) HWMODEL, 0, KV_STR_VALUE}, /* string KV */
};
size_t default_kv_table_nrs = 2;
#endif

void hwboard::led(u32 tick) {
    if (plc_var.info.plc_state < (u8) PLC_STATUS::Stopped) {
        led_run(tick & 0x80);
    }
    if (plc_var.info.plc_state > (u8) PLC_STATUS::Started) {
        led_run(tick & 0x40);
    }
}
